<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh" xml:lang="zh">
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>五度相生律和十二平均律的計算</title>
  <style>
    html {
      line-height: 1.5;
      font-family: Georgia, serif;
      font-size: 20px;
      color: #1a1a1a;
      background-color: #fdfdfd;
    }
    body {
      margin: 0 auto;
      max-width: 36em;
      padding-left: 50px;
      padding-right: 50px;
      padding-top: 50px;
      padding-bottom: 50px;
      hyphens: auto;
      word-wrap: break-word;
      text-rendering: optimizeLegibility;
      font-kerning: normal;
    }
    @media (max-width: 600px) {
      body {
        font-size: 0.9em;
        padding: 1em;
      }
    }
    @media print {
      body {
        background-color: transparent;
        color: black;
        font-size: 12pt;
      }
      p, h2, h3 {
        orphans: 3;
        widows: 3;
      }
      h2, h3, h4 {
        page-break-after: avoid;
      }
    }
    p {
      margin: 1em 0;
    }
    a {
      color: #1a1a1a;
    }
    a:visited {
      color: #1a1a1a;
    }
    img {
      max-width: 100%;
    }
    h1, h2, h3, h4, h5, h6 {
      margin-top: 1.4em;
    }
    h5, h6 {
      font-size: 1em;
      font-style: italic;
    }
    h6 {
      font-weight: normal;
    }
    ol, ul {
      padding-left: 1.7em;
      margin-top: 1em;
    }
    li > ol, li > ul {
      margin-top: 0;
    }
    blockquote {
      margin: 1em 0 1em 1.7em;
      padding-left: 1em;
      border-left: 2px solid #e6e6e6;
      color: #606060;
    }
    code {
      font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
      font-size: 85%;
      margin: 0;
    }
    pre {
      margin: 1em 0;
      overflow: auto;
    }
    pre code {
      padding: 0;
      overflow: visible;
    }
    .sourceCode {
     background-color: transparent;
     overflow: visible;
    }
    hr {
      background-color: #1a1a1a;
      border: none;
      height: 1px;
      margin: 1em 0;
    }
    table {
      margin: 1em 0;
      border-collapse: collapse;
      width: 100%;
      overflow-x: auto;
      display: block;
      font-variant-numeric: lining-nums tabular-nums;
    }
    table caption {
      margin-bottom: 0.75em;
    }
    tbody {
      margin-top: 0.5em;
      border-top: 1px solid #1a1a1a;
      border-bottom: 1px solid #1a1a1a;
    }
    th {
      border-top: 1px solid #1a1a1a;
      padding: 0.25em 0.5em 0.25em 0.5em;
    }
    td {
      padding: 0.125em 0.5em 0.25em 0.5em;
    }
    header {
      margin-bottom: 4em;
      text-align: center;
    }
    #TOC li {
      list-style: none;
    }
    #TOC a:not(:hover) {
      text-decoration: none;
    }
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    span.underline{text-decoration: underline;}
    div.column{display: inline-block; vertical-align: top; width: 50%;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    ul.task-list{list-style: none;}
    .display.math{display: block; text-align: center; margin: 0.5rem auto;}
  </style>

<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<!-- <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> -->
<script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.0/es5/tex-mml-chtml.min.js"></script>
<script>
  window.MathJax = {
    tex: {
      tags: 'ams'
    }
  };
</script>
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->

<style>
  .tips {
    border-radius: 4px;
    border: 1px solid darkslategray;
    padding-left: 1em;
    padding-right: 1em;
  }
</style>

<link rel="stylesheet" href="../../lib/jsnotebook/jsnotebook.css">
<script src="../../lib/jsnotebook/jsnotebook.js"></script>
<script src="./audio.js"></script>

<script>
window.onload = function() {
	jsnotebook_onload();
}
</script>

</head>
<body>
<header id="title-block-header">
<h1 class="title">五度相生律和十二平均律的計算</h1>
</header>
<ul>
<li><a href="#弁言">弁言</a></li>
<li><a href="#十二平均律">十二平均律</a></li>
<li><a href="#五度相生律">五度相生律</a></li>
<li><a href="#五度相生律和十二平均律的關係">五度相生律和十二平均律的關係</a></li>
<li><a href="#道場">道場</a></li>
<li><a href="#參考文獻">參考文獻</a></li>
</ul>
<h2 id="弁言">弁言</h2>
<p>音律是對音階到頻率的映射關係的約定。音樂則是不同時長的音階的排列組合。假如沒有音律，音階和頻率的對應關係是不確定的，那麼樂曲就難形成樂譜而流傳後世，樂者間就難以相互交流了。故而，設計合適的音律對於音樂的發展是必要的。經過漫長的歷史積累，人們已經在這方面積累了豐富經驗。最成功的音律莫過十二平均律和五度相生律。有趣的是，雖然分處世界各地，互相之間缺乏交流，但人們早期對音律的設計都體現出了相似的思想。通過了解各種音律的計算方式，我們能體會到古人對音樂的思考，鞏固樂理基礎。</p>
<div class="tips">
<p>「音樂則是不同時長的音階的排列組合。」是一種簡單的說法。一些音樂的細節是無法簡單由音階的頻率和時長完全描述的。</p>
</div>
<p>本文通過JavaScript代碼演示音律的計算。本文涉及的計算並不複雜，但還是希望讀者具備JavaScript的基礎，或者已經熟悉具有類似語法的編程語言（如Java、C）。</p>
<p>一旦點擊代碼塊下方的「運行」按鈕，瀏覽器就會運行對應程序。為了解釋代碼塊的使用，這裡先舉一個簡單的例子：</p>
<pre class="jsnb_block"><code>console.log(&quot;Hello world!!!&quot;);</code></pre>
<p>點擊按鈕，如果一切正常，你會看到上方輸出一行文字，這表明程序成功執行了。</p>
<p>文章將代碼分散在不同代碼塊中。有時在後續的代碼塊中要使用前面的數據，所以就借用<code>this</code>對象暫存感興趣的變量。例如：</p>
<pre class="jsnb_block"><code>this.a = 1; 
var a = 0;
console.log(&quot;參數導出完畢。&quot;);</code></pre>
<p>上面的代碼中有兩個<code>a</code>，但是只有<code>this.a</code>是可以在代碼塊間「傳遞」的。我們可以在下面的代碼塊中查看<code>this.a</code>的值：</p>
<pre class="jsnb_block"><code>this.a;</code></pre>
<p>以上是代碼塊的使用方式。閱讀本文時，建議讀者<strong>按順序</strong>運行各個代碼塊。代碼運行順序錯誤，或者遺漏幾個代碼塊沒有運行，可能導致意外結果。如果遇到程序問題，建議先刷新頁面。如果還是不行，可以換一個瀏覽器試試看。</p>
<p>本文代碼會產生聲音，建議提前降低音量或插上耳機。 另外，還請注意一點：本文中的代碼雖然可以修改，但修改無法保存！</p>
<p>代碼基礎就介紹到這裡。現在我們嘗試用代碼播放聲音。下面的代碼播放了一個頻率為440 Hz的音符：</p>
<pre class="jsnb_block"><code>playNote(440, 0.8);</code></pre>
<p>代碼中，<code>playNote</code>是我提前準備好的用於播放音符的函數。<code>playNote</code>的第一個參數是頻率，第二個參數是音符的持續時間。</p>
<p>為了方便播放一整段音樂，我還提供了<code>playNotes</code>函數。它接受<code>(音符、時間)</code>二元組構成的數組作為輸入。下面播放一段簡單的音符序列：</p>
<pre class="jsnb_block"><code>playNotes([[440, 0.4], [440, 0.4], [440, 0.8]]);</code></pre>
<div class="tips">
<p>如果聽不到聲音，可能是瀏覽器的兼容性有問題。建議使用最新版的Chrome瀏覽器，並開啟聲音播放權限。</p>
</div>
<h2 id="十二平均律">十二平均律</h2>
<p>人類的聽覺對於高頻聲音不敏感。聲音的頻率指數增長時，在人聽來，他們的「距離」卻是均勻增加的。例如，設<span class="math inline">\(f\)</span>是一常數，人耳會認為 <span class="math display">\[(f, kf, k^2f, k^3f \dots)\]</span> 這樣一組聲音是「等間距」的。</p>
<p>在十二平均律中，兩倍頻率的關係稱為<strong>八度關係</strong>，將一個頻率到其兩倍之間的範圍稱為一個<strong>八度</strong>。例如，頻率<span class="math inline">\(f\)</span>和<span class="math inline">\(2f\)</span>之間就是一個八度。將八度「十二等分」，就能得到十二平均律下的所有音階。</p>
<p>在八度上構造一個聽覺上等間距的十二段，相當於求一個比例為<span class="math inline">\(2^{1/12}\)</span>的等比數列。</p>
<p>記十二平均律下音階和頻率的映射函數為<code>f12()</code>，假設<code>f12(0)=440 Hz</code>，容易寫出<code>f12()</code>的實現：</p>
<pre class="jsnb_block"><code>const f12 = this.f12 = function(index) {
    return 440 * (2**(1/12 * index));
}

console.log(&quot;中央C（C4）：&quot; + f12(-9));
console.log(&quot;C5：&quot; + f12(3));</code></pre>
<p>鋼琴上的A4的頻率為440，等於<code>f12(0)</code>。不難推出中央C（C4）的頻率為<code>f12(-9)</code>，C5的頻率為<code>f12(3)</code>. 從代碼的輸出可見，<code>f12(3)</code>是<code>f12(-9)</code>的兩倍，恰好構成一個八度。</p>
<p>在十二平均律中，<span class="math inline">\(2^{1/12}\)</span>的頻率比例關係稱為<strong>半音</strong>，而<span class="math inline">\(2^{1/6}\)</span>的比例關係稱為<strong>全音</strong>。</p>
<p>以下代碼能以十二平均律的音階演奏一段樂曲：</p>
<pre class="jsnb_block"><code>const f12 = this.f12;

const t = 0.4;

playNotes([ 
    [f12(-5), 2 * t], [f12(-5), t], [f12(-2), t], [f12(0), t], [f12(3), t], [f12(3), t], 
    [f12(0), t], [f12(-2), 2 * t], [f12(-2), t], [f12(0), t], [f12(-2), 4 * t]
])</code></pre>
<h2 id="五度相生律">五度相生律</h2>
<p>雖然十二平均律在當代使用廣泛，但在遙遠的古代，最先形成的音律是五度相生律。西方的畢達哥拉斯首先提出了五度相生律。春秋時《管子》一書記載了生成音階的「三分損益法」，本質上與畢達哥拉斯法同屬一類，但做法上存在細微的差別。</p>
<p>五度相生律的提出基於這樣的觀察：頻率的比例關係越簡單，則音階的關係越「<strong>和諧</strong>」。在這裡，「簡單」意指頻率的關係可以用小的自然數的比例表示。在五度相生律中，音阶是按<span class="math inline">\(\frac{3}{2}\)</span>的比例以某个频率为基准生成的。以畢達哥拉斯的算法為例，首先通過不斷對基準頻率<span class="math inline">\(f\)</span>乘上<span class="math inline">\(\frac{3}{2}\)</span>，依次得到五個音階，再在基準頻率上乘以<span class="math inline">\(\frac{2}{3}\)</span>，又得到一個音階。加上基準頻率<span class="math inline">\(f\)</span>本身，就得到了七個音階。同時還需注意，一旦得到音階<span class="math inline">\(f'\)</span>，那麼<span class="math inline">\(f'\)</span>所有成二的整數次冪倍的頻率也被同時生成，即能同時得到<span class="math inline">\(\left\{\dots, \frac{1}{4}f, \frac{1}{2}f, 2f, 4f, 8f, \dots\right\}\)</span>。</p>
<p>下面的代碼演示了如何按五度相生律生成音階。</p>
<pre class="jsnb_block"><code>var coeff = []
for (var i = 0; i &lt; 7; ++i) {      // 一共只生成七個音階
    // 从 4/3*f 开始，依次乘以3/2
    var f = 4 / 3 * (3 / 2)**i;    
    // 總是將比例限制在1和2之間。之後只要將頻率翻倍/減半，就可以得到八度關係上的所有音階。
    while (f &gt;= 2) f = f / 2;      
    while (f &lt; 1) f = f * 2;
    // 打印
    if (coeff.length &gt; 0) {
        console.log(coeff[coeff.length - 1] + &quot;生成&quot; + f);
    }
    coeff.push(f);
}
coeff = coeff.sort();
console.log(&quot;\n五度相生律：&quot;);
console.log(coeff);
this.coeff5 = coeff;</code></pre>
<p>不難證明，五度相生律中，任意兩個頻率間總是有理數倍的關係。實際上，七個音階與基頻的比例為：</p>
<pre class="jsnb_block"><code> 
const coeff = [1, 9/8, 81/64, 4/3, 3/2, 27/16, 243/128];
console.log(coeff);</code></pre>
<p>在先前的代碼中，我們已經用<code>coeff5</code>變量存儲了包含七個頻率的一組音階。<code>f5()</code>函數假設<code>f5(0)=440 Hz</code>，通過加倍/減半<code>coeff5</code>中的頻率衍生出所有音階。</p>
<pre class="jsnb_block"><code>coeff5 = this.coeff5;

function mod(a, b) {
    return ((a % b) + b) % b;
}

const f5 = this.f5 = function(index) {
    index = index + 5;
    const p = Math.floor(index / 7);
    return 440 / coeff5[5] * coeff5[mod(index, 7)] * 2**p; 
}</code></pre>
<p>下方的代碼能以五度相生律的音階演奏一段樂曲。</p>
<pre class="jsnb_block"><code>const f5 = this.f5;

const t = 0.4;

playNotes([ 
    [f5(-3), 2 * t], [f5(-3), t], [f5(-1), t], [f5(0), t], [f5(2), t], [f5(2), t], 
    [f5(0), t], [f5(-1), 2 * t], [f5(-1), t], [f5(0), t], [f5(-1), 4 * t],
])</code></pre>
<p>五度相生律中的相鄰音階存在<strong>全音</strong>和<strong>半音</strong>兩種比例關係，分別是<span class="math inline">\(9/8=1.125\)</span>和<span class="math inline">\(\frac{256}{243}\approx 1.05\)</span>：</p>
<pre class="jsnb_block"><code>const f5 = this.f5;
for (var i = 0; i &lt; 7; ++i) {
    console.log(f5(i + 1) / f5(i));
}</code></pre>
<h2 id="五度相生律和十二平均律的關係">五度相生律和十二平均律的關係</h2>
<p>人們發現，有理數比的音更容易給人以「和諧」感，於是發明了五度相生律。五度相生律的缺點是，七個音階在八度上不是均勻分佈的。有時，樂師需要對樂曲作轉調處理，如果音階是等間隔分佈的話就會很方便。</p>
<p>明朝的朱載堉最早發明十二平均律。十二平均律一方面實現了音階的均勻分佈，另一方面又很好地兼容了五度相生律：如前文的代碼所示，五度相生律的樂曲，也能在十二平均律的音階上演奏。這是因為十二平均律產生的音階能在數值上很接近五度相生律。</p>
<p>十二平均律的問題則在於，相鄰半音間的比例不是有理數。這在製作、調試樂器時會比較困難。精確計算<span class="math inline">\(2^{1/12}\)</span>畢竟不是一件容易的事。</p>
<h2 id="道場">道場</h2>
<pre class="jsnb_block"><code>// 就寫到這裡。最後留一個格子專門給讀者實驗用吧。</code></pre>
<h2 id="參考文獻">參考文獻</h2>
</body>
</html>
